package com.cblz.bd.server.core.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cblz.bd.common.core.constant.BdAdminConstant;
import com.cblz.bd.common.core.entity.BaAdminGroup;
import com.cblz.bd.common.core.entity.BaMenuRule;
import com.cblz.bd.common.core.entity.system.AdminGroupTree;
import com.cblz.bd.common.core.entity.system.IndexSearchParams;
import com.cblz.bd.common.core.exception.BdAdminException;
import com.cblz.bd.common.core.utils.TreeUtil;
import com.cblz.bd.server.core.annotation.Log;
import com.cblz.bd.server.core.entity.vo.BaAdminGroupVO;
import com.cblz.bd.server.core.event.ClearCacheEventListen;
import com.cblz.bd.server.core.event.MenuRulesEditEventListen;
import com.cblz.bd.server.core.mapper.BaAdminGroupMapper;
import com.cblz.bd.server.core.service.BaAdminGroupService;
import com.cblz.bd.server.core.service.BaMenuRuleService;
import com.google.common.collect.Sets;
import io.vavr.control.Option;
import io.vavr.control.Try;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yanjieping
 * @description 针对表【ba_admin_group(管理分组表)】的数据库操作Service实现
 * @createDate 2022-11-04 15:09:53
 */
@Slf4j
@Service("baAdminGroupService")
@RequiredArgsConstructor(onConstructor_ = {@Lazy})  // 解决循环依赖
public class BaAdminGroupServiceImpl extends ServiceImpl<BaAdminGroupMapper, BaAdminGroup>
        implements BaAdminGroupService {

    private final BaMenuRuleService baMenuRuleService;

    private final ApplicationEventPublisher publisher;

    /**
     * 根据登录ID拿到用户权限
     *
     * @param loginId
     * @return
     */
    @Override
    public List<BaAdminGroup> getBdAdminRulesByLoginId(Integer loginId) {
        return this.baseMapper.getBdAdminRulesByLoginId(loginId);
    }

    /**
     * 拿到所有管理员分组的树形结构或根据组名模糊查询的属性结构
     *
     * @return
     */
    @Override
    public List<AdminGroupTree> getAllGroupInfoTree(BaAdminGroup baAdminGroup) {
        if (Objects.isNull(baAdminGroup)) {
            //拿到所有管理员分组的树形结构
            return getAllGroupInfoTree((BaAdminGroup) null);
        }
        //根据组名模糊查询的属性结构
        return getAllGroupInfoTreeByParams(baAdminGroup);
    }

    /**
     * 根据主键ID拿到管理员分组的树形结构
     *
     * @return
     */
    @Override
    public List<AdminGroupTree> getAllGroupInfoTree(IndexSearchParams indexSearchParams) {
        List<BaAdminGroup> list;
        if (!Objects.isNull(indexSearchParams)
                && StringUtils.isNotBlank(indexSearchParams.getInitKey())
                && StringUtils.isNotBlank(indexSearchParams.getInitValue())
                && indexSearchParams.getInitValues() != null)
            if (indexSearchParams.getInitValues().isEmpty())
                list = this.list(new LambdaQueryWrapper<BaAdminGroup>().eq(BaAdminGroup::getId, indexSearchParams.getInitValue()));
            else
                list = this.list(new LambdaQueryWrapper<BaAdminGroup>().in(BaAdminGroup::getId, indexSearchParams.getInitValues()));
        else
            list = this.list();

        return getAdminGroupTrees(indexSearchParams, list);
    }

    @NotNull
    private List<AdminGroupTree> getAdminGroupTrees(IndexSearchParams indexSearchParams, List<BaAdminGroup> list) {
        /*
         * 将查询出的数据权限列处理成和前端对应的数据
         */
        List<BaAdminGroup> adminGroupList = Option.of(list).getOrNull().stream().map(group -> {
            String[] rules = Option.of(group.getRules()).getOrNull().split(BdAdminConstant.SYSTEM_COMMA.getVal());
            if (StringUtils.isBlank(rules[0])) {
                group.setRules("没有分配权限");
                return group;
            }
            if (StringUtils.equals("*", rules[0])) {
                group.setRules("超级管理员");
                return group;
            }
            BaMenuRule infoByMenuId = baMenuRuleService.getInfoByMenuId(Integer.valueOf(rules[0]));
            group.setRules(infoByMenuId.getTitle() + " " + rules.length + " 项");
            return group;
        }).toList();

        if (adminGroupList.isEmpty())
            return Collections.emptyList();
        List<AdminGroupTree> adminGroupTrees = new ArrayList<>();
        buildTrees(adminGroupTrees, adminGroupList);

        List<AdminGroupTree> buildTree = (List<AdminGroupTree>) TreeUtil.build(adminGroupTrees);
        if (!buildTree.isEmpty()) {
            // 如果是选择器需要的数据，那么将树形菜单转成List
            if (!Objects.isNull(indexSearchParams)
                    && Boolean.TRUE.equals(indexSearchParams.getSelect())) {
                List<AdminGroupTree> selectList = new ArrayList<>();
                TreeUtil.recursionTreeToList(buildTree, selectList);
                return selectList;
            }
            return buildTree;
        }

        return Collections.emptyList();
    }

    /**
     * 监听菜单权限的数据改动，再对角色组所拥有对应的权限进行相同改动
     *
     * @param menuRulesEditEventListen
     */
    @Override
    @Log(description = "更新角色组权限")
    @Transactional
    @TransactionalEventListener(classes = MenuRulesEditEventListen.class, phase = TransactionPhase.AFTER_COMMIT)
    public void menuRulesEditEvent(MenuRulesEditEventListen menuRulesEditEventListen) {
        Set<Integer> menuRulesIds = Option.of(baMenuRuleService.list()).getOrElseThrow(() -> new BdAdminException("获取菜单信息失败，查询结果为空")).stream().map(BaMenuRule::getId).collect(Collectors.toSet());

        List<BaAdminGroup> baAdminGroups = Option.of(this.list()).getOrElseThrow(() -> new BdAdminException("获取角色组信息失败，查询结果为空"));

        baAdminGroups.forEach(group -> {
            if (!StringUtils.containsAny(group.getRules(), "*")) {
                Set<Integer> groupRules = Arrays.stream(group.getRules().split(BdAdminConstant.SYSTEM_COMMA.getVal())).map(Integer::valueOf).collect(Collectors.toSet());
                // 得到这两个集合的交集
                Sets.SetView<Integer> intersection = Sets.intersection(groupRules, menuRulesIds);
                // 如果交集的大小小于角色拥有的权限组大小，那么代表某个菜单或权限按钮被删除或修改状态了
                if (intersection.size() < groupRules.size()) {
                    // 拿到角色权限组和交集结果的差集，得到被删除的菜单权限
                    Sets.SetView<Integer> difference = Sets.difference(groupRules, intersection);
                    log.warn("角色组:「 {} 」中的菜单权限ID为: {} 的数据被删除，现更新对应的角色组权限信息......", group.getName(), difference);

                    String rules = intersection.stream().map(String::valueOf).collect(Collectors.joining(BdAdminConstant.SYSTEM_COMMA.getVal()));

                    group.setRules(rules);
                }
            }
        });

        Try.of(() -> updateBatchById(baAdminGroups))
                .onFailure(error -> log.error("更新对应的角色组权限信息失败......Error"));
        log.info("更新对应的角色组权限信息完成......Success");

        publisher.publishEvent(new ClearCacheEventListen(this));
    }

    private void buildTrees(List<AdminGroupTree> trees, List<BaAdminGroup> groups) {
        groups.forEach(group -> trees.add(BeanUtil.copyProperties(group, AdminGroupTree.class)));
    }

    /**
     * 角色组新增、修改
     *
     * @param baAdminGroupVO
     */
    @Override
    @Log(description = "角色组新增、修改")
    public void addOrUpdateGroup(BaAdminGroupVO baAdminGroupVO) {
        BaAdminGroup baAdminGroup = new BaAdminGroup();
        BeanUtils.copyProperties(baAdminGroupVO, baAdminGroup, new String[]{"rules"});
        baAdminGroup.setRules(StringUtils.join(baAdminGroupVO.getRules(), BdAdminConstant.SYSTEM_COMMA.getVal()));
        if (baAdminGroupVO.getId() == null) {
            // 新增
            baAdminGroup.setCreatetime((long) LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
            this.baseMapper.addGroup(baAdminGroup);
        } else {
            // 修改
            baAdminGroup.setUpdatetime((long) LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
            this.baseMapper.updateGroup(baAdminGroup);
        }
    }


    @Override
    public BaAdminGroup getGroupById(Integer id) {
        return this.baseMapper.getGroupById(id);
    }

    public Boolean checkHasLevel(Integer id) {
        int checkNum = this.baseMapper.countIsHasLevel(id);
        if (checkNum > 0) {
            return true;
        }
        return false;
    }

    public Boolean countIsBindAdmin(Integer id) {
        int checkBindUserNum = this.baseMapper.countIsBindAdmin(id);
        if (checkBindUserNum > 0) {
            return true;
        }
        return false;
    }

    public List<AdminGroupTree> getAllGroupInfoTreeByParams(BaAdminGroup baAdminGroup) {
        List<BaAdminGroup> list;
        if (!Objects.isNull(baAdminGroup)
                && StringUtils.isNotBlank(baAdminGroup.getName())) {
            list = this.list(new LambdaQueryWrapper<BaAdminGroup>().like(BaAdminGroup::getName, baAdminGroup.getName()));
        } else {
            list = this.list();
        }
        IndexSearchParams indexSearchParams = new IndexSearchParams();
        return getAdminGroupTrees(indexSearchParams, list);
    }

    @Override
    public Boolean checkHasName(Integer id, String name) {
        List<BaAdminGroup> list;
        if (id > 0) {//修改
            list = this.list(new LambdaQueryWrapper<BaAdminGroup>().eq(BaAdminGroup::getName, name).ne(BaAdminGroup::getId, id));
        } else {//新增
            list = this.list(new LambdaQueryWrapper<BaAdminGroup>().eq(BaAdminGroup::getName, name));
        }
        if (list.size() > 0) {
            return true;
        }
        return false;
    }

    @Override
    public HashMap<String, Object> checkDelete(Integer[] idsArray) {
        HashMap<String, Object> checkGroupData = new HashMap();
        checkGroupData.put("isAllow", true);
        checkGroupData.put("msg", "");
        //1:超级管理组 2:一级管理员 系统默认组，不能删除
        if (Arrays.asList(idsArray).contains(1)) {
            checkGroupData.put("isAllow", false);
            checkGroupData.put("msg", "超级管理组不能删除！");
            return checkGroupData;
        }
        if (Arrays.asList(idsArray).contains(2)) {
            checkGroupData.put("isAllow", false);
            checkGroupData.put("msg", "一级管理员不能删除！");
            return checkGroupData;
        }
        for (Integer id : idsArray) {
            //判断是否存在下级
            Boolean isHasLevel = this.checkHasLevel(id);
            if (isHasLevel == true) {
                checkGroupData.put("isAllow", false);
                checkGroupData.put("msg", "此角色存在下级，不能删除！");
                break;
            }
            //判断是否有管理人员隶属于此角色
            Boolean isBindAdmin = this.countIsBindAdmin(id);
            if (isBindAdmin == true) {
                checkGroupData.put("isAllow", false);
                checkGroupData.put("msg", "此角色已经有用户关联，不能直接删除，请先解绑用户！");
                break;
            }
        }
        return checkGroupData;
    }

}




